home *** CD-ROM | disk | FTP | other *** search
Wrap
/* KeyGenerator.m created by cfeder on Wed 26-Oct-1994 */ #import "KeyGenerator.h" #import "ValueForKey.h" // Default implementation for all EOs to generate their own primary keys @implementation NSObject (assignPrimaryKey) - (void)assignPrimaryKeyForEntity:(EOEntity *)entity { NSString *primaryKeyName = [[entity primaryKeyAttributeNames] objectAtIndex:0]; NSNumber *value = [NSNumber numberWithUnsignedInt:[KeyGenerator nexKeyForEntity:entity]]; NSDictionary *pkDict = [NSDictionary dictionaryWithObjects:&value forKeys:&primaryKeyName count:1]; [self takeValuesFromDictionary:pkDict]; } - (void)assignPrimaryKeyIfNotAlreadyPresentForEntity:(EOEntity *)entity { // Figure out whether we already have a key assigned BOOL hasKey = NO; NSEnumerator *keys = [[entity primaryKeyAttributeNames] objectEnumerator]; NSString *key; while (key = [keys nextObject]) { id value = [self valueForKey:key]; if (value && (value != [EONull null]) && (![value isKindOfClass:[NSNumber class]] || ([value intValue]!=0))){ hasKey = YES; break; } } // if we don't already have a primary key, then assign ourselves one // get our current primary key values if (!hasKey) { [self assignPrimaryKeyForEntity:entity]; } } @end // Assumptions: // Model contains: // - login information // - UniqueKey entity with attributes entity_name, and max_key @implementation KeyGenerator #define RESERVE_SET_SIZE 5 - initWithEntity:(EOEntity *)anEntity dataSource:(EODatabaseDataSource *)aDataSource { [super init]; entity = [anEntity retain]; dataSource = [aDataSource retain]; lastGranted = lastReserved = 0; return self; } - (unsigned)nextKey { if (lastGranted >= lastReserved) { // Go out to database and reserve a block of keys EOQualifier *tableNameQualifier; EODatabaseChannel *channel = [dataSource databaseChannel]; EODatabaseContext *context = [channel databaseContext]; NSString *externalName = [entity externalName]; EOEntity *uniqueKeyEntity = [dataSource entity]; if (!uniqueKeyEntity) [NSException raise:NSInternalInconsistencyException format:@"%s: model does not contain UniqueKey entity '%@'", sel_getName(_cmd), [entity name]]; if (![channel isOpen]) [channel openChannel]; [context beginTransaction]; // This is the first time for this entity. We need to get the current setting. if (!currentMaxRecord) { // Construct the qualifier for our name tableNameQualifier = [[[EOQualifier alloc] initWithEntity:uniqueKeyEntity qualifierFormat:@"%A= '%@'", @"entity_name", externalName] autorelease]; [channel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil]; currentMaxRecord = [[channel fetchWithZone:[self zone]] retain]; [channel cancelFetch]; } if (currentMaxRecord) { // attempt to update the max_key value for(;;) { lastGranted = [[currentMaxRecord objectForKey:@"max_key"] unsignedIntValue]; lastReserved = lastGranted + RESERVE_SET_SIZE; [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"]; if ([channel updateObject:currentMaxRecord] && [context commitTransaction]) break; // success!! // Update failed. Setup for another pass [context rollbackTransaction]; [context beginTransaction]; [channel refetchObject:currentMaxRecord]; } } else { // we have to insert a record for this entity currentMaxRecord = [[EOGenericRecord alloc] initWithPrimaryKey:nil entity:uniqueKeyEntity]; // retained [currentMaxRecord setObject:externalName forKey:@"entity_name"]; lastReserved = RESERVE_SET_SIZE; lastGranted = 0; [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"]; NSLog(@"Entity '%@' not in UniqueKey table. Adding record: %@", [entity name], currentMaxRecord); if (![channel insertObject:currentMaxRecord]) [NSException raise:NSInternalInconsistencyException format:@"Failed to insert UniqueKey object for entity: %@", [entity name]]; [context commitTransaction]; } } lastGranted++; return lastGranted; } - (void)dealloc { [entity release]; [dataSource release]; [super dealloc]; } // // Class methods to automatically cache instances // static NSMutableDictionary *entityNameToGenerator; + (EODatabaseDataSource *)dataSourceForModel:(EOModel *)model { // use the datasource rendevous mechanism to do our caching for us return [[EODatabaseDataSource alloc] initWithModelName:[model name] entityName:@"unique_key" databaseName:nil contextName:@"_UniqueKey" channelName:nil]; } + keyGeneratorForEntity:(EOEntity *)anEntity { KeyGenerator *kg = [entityNameToGenerator objectForKey:[anEntity name]]; if (!kg) { EODatabaseDataSource *ds = [self dataSourceForModel:[anEntity model]]; if (!ds) [NSException raise:NSInternalInconsistencyException format:@"%s: unable to allocation UniqueKey data source for entity '%@'", sel_getName(_cmd), [anEntity name]]; kg = [[KeyGenerator alloc] initWithEntity:anEntity dataSource:ds]; if (kg) { if (!entityNameToGenerator) entityNameToGenerator = [NSMutableDictionary new]; [entityNameToGenerator setObject:kg forKey:[anEntity name]]; } } return kg; } + (unsigned)nexKeyForEntity:(EOEntity *)anEntity { KeyGenerator *kg = [self keyGeneratorForEntity:anEntity]; if (!kg) [NSException raise:NSInternalInconsistencyException format:@"%s: unable to create KeyGenerator for entity '%@'", sel_getName(_cmd), [anEntity name]]; return [kg nextKey]; } @end